package org.sfx.rich

import org.sfx.rich.Enrich._
import java.lang.reflect.GenericArrayType
import java.lang.reflect.ParameterizedType
import java.lang.reflect.WildcardType
import java.lang.reflect.Type
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.value.ObservableValue
import javafx.beans.value.WritableValue
import javafx.beans.property.SimpleStringProperty

class RichType(underlying: java.lang.reflect.Type) {
  def scalaString: String = {
    underlying match {
      case clazz: Class[Any] => {
        if (clazz.isArray()) {
          "Array[" + clazz.getComponentType().scalaString + "]"
        } else if (clazz.isPrimitive()) {
          underlying match {
            case java.lang.Boolean.TYPE => "Boolean"
            case java.lang.Character.TYPE => "Character"
            case java.lang.Byte.TYPE => "Byte"
            case java.lang.Short.TYPE => "Short"
            case java.lang.Integer.TYPE => "Int"
            case java.lang.Long.TYPE => "Long"
            case java.lang.Float.TYPE => "Float"
            case java.lang.Double.TYPE => "Double"
            case java.lang.Void.TYPE => "Void"
            case _ => assert(false); "<UNKNOWN>"
          }
        } else {
          clazz.getName()
        }
      }
      case parameterizedType: ParameterizedType => {
        val rawType = parameterizedType.getRawType()
        val subTypes = parameterizedType.getActualTypeArguments().toList
        rawType.scalaString + "[" +
        subTypes.map(_.scalaString).reduceLeft(_ + ", " + _) + "]"
      }
      case wildcardType: WildcardType => {
        if (wildcardType.getLowerBounds().length != 0) {
          "_ >: " + wildcardType.getLowerBounds()(0).scalaString
        } else {
          "_ <: " + wildcardType.getUpperBounds()(0).scalaString
        }
      }
      case _ => "<" + underlying + ": " + underlying.getClass() + ">"
    }
  }
  
  def richKind: String = "Type"
  
  //def <:<(that: Type): Boolean = {
    //return com.googlecode.gentyref.GenericTypeReflector.isSuperType(that, underlying)
    /*import org.sfx.util.Console._
    out.println("--> 1 testing " + underlying + " <:< " + that)
    out.indent {
      if (underlying == that) {
        out.println("--> 2 " + underlying + " <:< " + that + " = true")
        true
      } else {
        underlying match {
          case underlyingClass: Class[_] => {
            {
              underlyingClass.genericSuperClass.foldLeft(false) { (acc, mySuper) =>
                acc || (mySuper <:< that)
              }
            } || {
              underlyingClass.genericInterfaces.foldLeft(false) { (acc, interface) =>
                acc || (interface <:< that)
              }
            }
          }
          case underlyingParameterizedType: ParameterizedType => {
            that match {
              case thatParameterizedType: ParameterizedType => {
                {
                  {
                    underlyingParameterizedType.rawType == thatParameterizedType.rawType
                  } && {
                    {
                      thatParameterizedType.rawType == underlyingParameterizedType.rawType
                    } && {
                      val list1 = thatParameterizedType.actualTypeArguments.toList
                      val list2 = underlyingParameterizedType.actualTypeArguments.toList
                      val result = list1.zip(list2).foldLeft(true)({ (acc, pair) => acc && pair._1 == pair._2 })
                      out.println("--> 4 " + list1 + " <> " + list2 + " result2: " + result)
                      result
                    }
                  }
                } || {
                  underlyingParameterizedType.rawType match {
                    case underlyingRawClass: Class[_] => {
                      out.println("--> underlying raw class: " + underlyingRawClass)
                      for (underlyingSuperClass <- underlyingRawClass.genericSuperClass) {
                        out.println("--> underlying super class: " + underlyingSuperClass)
                      }
                      for (underlyingSuperInterface <- underlyingRawClass.genericInterfaces) {
                        out.println("--> underlying super interface: " + underlyingSuperInterface)
                      }
                      false
                    }
                    case underlyingUknown => {
                      throw new RuntimeException("not implemented for: " + underlyingUknown)
                    }
                  }
                }
              }
              case _ => {
                throw new RuntimeException("not implemented for: " + underlying.getClass())
              }
            }
          }
          case _ => {
            throw new RuntimeException("not implemented for: " + underlying.getClass())
          }
        }
      }
    }*/
  //}
}

object RichType {
  class Thing1 {
    def booleanProperty1 = new SimpleBooleanProperty()
    def booleanProperty2: WritableValue[Boolean] = null
  }
  
  def main(args: Array[String]): Unit = {
    /*val thing1Class = classOf[Thing1]
    val thing1Method1 = thing1Class.method("booleanProperty1")
    val thing1Return1 = thing1Method1.genericReturnType
    println(thing1Return1)

    val thing1Method2 = thing1Class.method("booleanProperty2")
    val thing1Return2 = thing1Method2.genericReturnType
    println(thing1Return2)
    
    println(thing1Return1 <:< thing1Return2)*/
    
    class MyClass {
      def field1: java.util.List[Boolean] = null
      def field2: java.util.List[java.lang.Boolean] = null
    }
    
    //scala.runtime.Boolean
    
    println(classOf[MyClass].getMethod("field1").getGenericReturnType())
    println(classOf[MyClass].getMethod("field2").getGenericReturnType())
  }
}
